#include <pthread.h>
#include <stdlib.h>
#include <string.h>
#include "pd_transport.h"
#include "pd_log.h"
#include "pd_event.h"

struct PdTransport
{
    PdSocketEvent *ev;
    pthread_t pd;
    int stop_flag;
    pd_on_interval_pt on_interval_func;
    void *on_interval_arg;
};

static PdTransport *alloc_();

static void free_(PdTransport *sock);

static void *thread_loop_(void *data);

////////////////////////////////////////////////////////////////////////////////////////////////////

PdTransport *alloc_()
{
    PdTransport *ret = (PdTransport*)malloc(sizeof(PdTransport));
    if (NULL != ret)
    {
        memset(ret, 0, sizeof(PdTransport));
    }
    return ret;
}

void free_(PdTransport *ts)
{
    if (NULL != ts)
    {
        free(ts);
    }
}

void *thread_loop_(void *data)
{
    PdTransport *ts = (PdTransport*)data;
    PdIOEvent events[MAX_SOCKET_EVENTS];
    while (!ts->stop_flag)
    {
        if (NULL != ts->on_interval_func)
        {
            ts->on_interval_func(ts, ts->on_interval_arg);
        }
        int ret = pd_event_get_events(ts->ev, TRANSPORT_LOOP_INTERVAL, events, MAX_SOCKET_EVENTS);
        int i = 0;
        for (; i < ret; i++)
        {
            if (events[i].readable
                    && NULL != events[i].ioc->on_readable)
            {
                events[i].ioc->on_readable(events[i].ioc);
                PD_LOG(DEBUG, "on readable done, ioc=%p", events[i].ioc);
            }
            if (events[i].writeable
                    && NULL != events[i].ioc->on_writeable)
            {
                events[i].ioc->on_writeable(events[i].ioc);
                PD_LOG(DEBUG, "on writeable done, ioc=%p", events[i].ioc);
            }
            if (events[i].error
                    && NULL != events[i].ioc->on_error)
            {
                events[i].ioc->on_error(events[i].ioc);
                PD_LOG(DEBUG, "on error done, ioc=%p", events[i].ioc);
            }
        }
    }
    return NULL;
}

////////////////////////////////////////////////////////////////////////////////////////////////////

PdTransport *pd_transport_init()
{
    PdTransport *ret = alloc_();
    if (NULL == ret)
    {
        PD_LOG(WARN, "alloc transport fail");
    }
    else if (NULL == (ret->ev = pd_event_init()))
    {
        PD_LOG(WARN, "init PdSocketEvent fail, ts=%p", ret);
        free_(ret);
        ret = NULL;
    }
    else
    {
        ret->pd = 0;
        ret->stop_flag = 0;
        ret->on_interval_func = NULL;
        ret->on_interval_arg = NULL;
        PD_LOG(INFO, "transport init succ, ts=%p ev=%p", ret, ret->ev);
    }
    return ret;
}

void pd_transport_destroy(PdTransport *ts)
{
    if (NULL == ts)
    {
        PD_LOG(WARN, "transport null pointer");
    }
    else
    {
        pd_transport_stop(ts);
        pd_transport_wait(ts);
        ts->on_interval_arg = NULL;
        ts->on_interval_func = NULL;
        ts->stop_flag = 0;
        ts->pd = 0;
        if (NULL != ts->ev)
        {
            pd_event_destroy(ts->ev);
            ts->ev = NULL;
        }
        free_(ts);
    }
}

void pd_transport_set_timer(PdTransport *ts, pd_on_interval_pt on_interval, void *arg)
{
    if (NULL == ts)
    {
        PD_LOG(WARN, "transport null pointer");
    }
    else
    {
        ts->on_interval_func = on_interval;
        ts->on_interval_arg = arg;
    }
}

int pd_transport_run(PdTransport *ts)
{
    int ret = 0;
    int tmp_ret = 0;
    if (NULL == ts)
    {
        PD_LOG(WARN, "transport null pointer");
        ret = -1;
    }
    else
    {
        ts->stop_flag = 0;
        if (0 != (tmp_ret = pthread_create(&ts->pd, NULL, thread_loop_, (void*)ts)))
        {
            PD_LOG(WARN, "pthread_create fail, ts=%p errno=%d", ts, tmp_ret);
            ret = -1;
        }
        else
        {
            PD_LOG(INFO, "pthrad_create succ, ts=%p pd=%ld", ts, ts->pd);
        }
    }
    return ret;
}

int pd_transport_stop(PdTransport *ts)
{
    int ret = 0;
    if (NULL == ts)
    {
        PD_LOG(WARN, "transport null pointer");
        ret = -1;
    }
    else
    {
        ts->stop_flag = 1;
    }
    return ret;
}

int pd_transport_wait(PdTransport *ts)
{
    int ret = 0;
    if (NULL == ts)
    {
        PD_LOG(WARN, "transport null pointer");
        ret = -1;
    }
    else
    {
        if (0 != ts->pd)
        {
            pthread_join(ts->pd, NULL);
            ts->pd = 0;
        }
    }
    return ret;
}

int pd_transport_add_ioc(PdTransport *ts, PdIOComponent *ioc, int enable_read, int enable_write)
{
    int ret = 0;
    if (NULL == ts)
    {
        PD_LOG(WARN, "transport null pointer");
        ret = -1;
    }
    else if (NULL == ioc)
    {
        PD_LOG(WARN, "invalid param, ioc=%p", ioc);
        ret = -1;
    }
    else if (0 != (ret = pd_event_add_event(ts->ev, ioc, enable_read, enable_write)))
    {
        PD_LOG(WARN, "add ioc fail, ts=%p ev=%p ioc=%p enable_read=%d enable_write=%d",
                ts, ts->ev, ioc, enable_read, enable_write);
    }
    else
    {
        PD_LOG(INFO, "add ioc succ, ts=%p ev=%p ioc=%p enable_read=%d enable_write=%d",
                ts, ts->ev, ioc, enable_read, enable_write);
    }
    return ret;
}

int pd_transport_set_ioc(PdTransport *ts, PdIOComponent *ioc, int enable_read, int enable_write)
{
    int ret = 0;
    if (NULL == ts)
    {
        PD_LOG(WARN, "transport null pointer");
        ret = -1;
    }
    else if (NULL == ioc)
    {
        PD_LOG(WARN, "invalid param, ioc=%p", ioc);
        ret = -1;
    }
    else if (0 != (ret = pd_event_set_event(ts->ev, ioc, enable_read, enable_write)))
    {
        PD_LOG(WARN, "set ioc fail, ts=%p ev=%p ioc=%p enable_read=%d enable_write=%d",
                ts, ts->ev, ioc, enable_read, enable_write);
    }
    else
    {
        PD_LOG(INFO, "set ioc succ, ts=%p ev=%p ioc=%p enable_read=%d enable_write=%d",
                ts, ts->ev, ioc, enable_read, enable_write);
    }
    return ret;
}

int pd_transport_remove_ioc(PdTransport *ts, PdIOComponent *ioc)
{
    int ret = 0;
    if (NULL == ts)
    {
        PD_LOG(WARN, "transport null pointer");
        ret = -1;
    }
    else if (NULL == ioc)
    {
        PD_LOG(WARN, "invalid param, ioc=%p", ioc);
        ret = -1;
    }
    else if (0 != (ret = pd_event_remove_event(ts->ev, ioc)))
    {
        PD_LOG(WARN, "remove ioc fail, ts=%p ev=%p ioc=%p",
                ts, ts->ev, ioc);
    }
    else
    {
        PD_LOG(INFO, "remove ioc succ, ts=%p ev=%p ioc=%p",
                ts, ts->ev, ioc);
    }
    return ret;
}

